
import time
import os
import socket

import machine


html=""
gzip=False
#  cors
#    Access-Control-Allow-Origin: *
## HTTP/1.1 Keep-Alive; 1.0没有
HEADER_CORS='Access-Control-Allow-Origin: *\r\n'
STATUS_OK='HTTP/1.0 200 OK\r\n'
STATUS_30X='HTTP/1.0 302 Moved\r\n'
STATUS_404='HTTP/1.0 404 NotFound\r\n'
HEADER_TYPE_JSON='Content-type: application/json\r\n'
HEADER_TYPE_HTML='Content-type: text/html\r\n'
HEADER_GZIP_ENCODING='Content-Encoding: gzip\r\n'
# HEADER_TYPE_JSON='Content-type: text/html\r\n'
# HEADER_TYPE_JSON='Content-type: application/json\r\n'
HEADER_END='\r\n'
# 

def file_exists(filename):
    try:
        os.stat(filename)
        return True
    except OSError:
        return False

def get_conf_file(key):
    return "_conf_"+key+".txt"

def KV_write(key,value=None):
    if value is None:
        return True
    try:
        with open(get_conf_file(key),"w") as f:
            f.write(key)
            f.write("=")
            if isinstance(value,str):
                f.write(value)
            else:
                f.write("not_string")
            f.write("\n")
            return True
    except Exception as e:
        print(e)
        return False

def KV_get(key,defaultValue=None):
    try:
        with open(get_conf_file(key),"r") as f:
            lines=f.readlines()
            for line in lines:
                line=line.strip('\r\n  ')
                kv=line.split("=")
                if kv[0]==key:
                    if len(kv)==1:
                        return "null"
                    if len(kv)==2:
                        return kv[1]
                    if len(kv)>2:
                        return "=".join(kv[1:])
    except Exception as e:
        print(e)
    return defaultValue


def read_mainpage():
    global html,gzip
    if html is None or html == "":
        try:
            name="index.html"
            if file_exists("index.html.gz"):
                name="index.html.gz"
                gzip=True
            print("[info] read_mainpage: html = ",name)
            with open(name,"rb") as f:
                # read all 
                html=f.read()
        except Exception as e:
            print("[E] read_mainpage error: html = ",name)
            print(e)
            pass

# json_str=my_agent.get_info(event="/",force=False)
# response = html % (json_str)

# if gzip:
#     cl.send('HTTP/1.0 200 OK\r\nContent-type: text/html\r\nContent-Encoding: gzip\r\n\r\n')
# else:
#     cl.send('HTTP/1.0 200 OK\r\nContent-type: text/html\r\n\r\n')
def response_ok(cl):
    cl.send(STATUS_OK)
    cl.send(HEADER_CORS)

def response_30x(cl):
    cl.send(STATUS_30X)
    cl.send(HEADER_CORS)

def response_404(cl):
    cl.send(STATUS_404)
    cl.send(HEADER_CORS)

def response_json(cl,json_str=None):
    # func()
    # response = my_agent.get_info(event=url,force=force)
    response_ok(cl)
    cl.send(HEADER_TYPE_JSON)
    if isinstance(json_str, str):
        cl.send("Content-Length: %d\r\n"%(len(json_str)))
        cl.send(HEADER_END)
        cl.send(json_str)

def mk_default_handler(my_agent):
    def handler(cl):
        response_ok(cl)
        cl.send(HEADER_TYPE_HTML)
        if gzip:
            cl.send(HEADER_GZIP_ENCODING)
        cl.send(HEADER_END)
        cl.send(html)
    return handler

def mk_404_handler(my_agent):
    def handler(cl):
        response_404(cl)
        cl.send(HEADER_TYPE_HTML)
        cl.send(HEADER_END)
    return handler

HANDLERS={}

def _web_server(s):
    while True:
        cl, addr = s.accept()
        print('client connected from', addr)
        cl_file = cl.makefile('rwb', 0)
        method=""
        path=""
        cl.settimeout(5)
        try:
            for _ in range(100):
                line = cl_file.readline()
                if not line or line == b'\r\n':
                    break
                if method=="":
                    line0=line.decode()
                    line0=line0.split(" ")
                    if len(line0) <2:
                        continue
                    method=line0[0].lower()
                    path=line0[1].lower()
                    
            if method=="" or (method!="get" and method !="post"):
                continue
            print("method=%s, path=%s"%(method,path))
            h=HANDLERS.get(path)
            if not callable(h):
                h=HANDLERS.get("*")
            if callable(h):
                h(cl)
            print("[i] connect done")
            cl.close()
        except Exception as e:
            print("[E] web connect error ? or timeout:",e)
            try:
                cl.close()
            except Exception as e:
                print("[E] web 'cl.close()' error",e)


    # void handle404()
    # {
    #     defaultNetwork->websrv.send(404, "text/html", str404);
    # }

urls_info = "/sun_water_info.json"
urls_feed ="/sun_water_feed"
urls_full_feed ="/sun_water_full_feed"
urls_cancelfeed = "/sun_water_cancel_feed"
urls_heat = "/sun_water_heat"
urls_heat_75 = "/sun_water_heat75"
urls_cancelheat = "/sun_water_cancel_heat"

urls_history = "/sun_water_history"


# cl.send('HTTP/1.0 200 OK\r\nContent-type: application/json\r\n\r\n')
# history
def mk_handler_json(func):
    def handler(cl):
        data=func()
        try:
            json_str='{'
            count=0
            for k in data.keys():
                if count==0:
                    count=1
                else:
                    json_str+=','
                # data[k] == 数值 || 列表[元素==数值]
                json_str+='"%s":%s'%(k,str(data[k]))
            json_str+='}'
        except Exception as e:
            json_str='{}'
            print("[E] send history error",e)
        response_json(cl,json_str)
    return handler

def mk_handler(my_agent,url,force,func=None):
    def hand(cl):
        response = my_agent.get_info(event=url,force=force)
        response_json(cl,response)

    def hand_with_func(cl):
        func()
        response = my_agent.get_info(event=url,force=force)
        response_json(cl,response)

    if callable(func):
        HANDLERS[url]=hand_with_func
    else:
        HANDLERS[url]=hand        

def response_action(cl,key,code,value=None):
    if value is None:
        return response_json(cl,'{"code":"%s","action":"%s"}'%(code,key))
    response_json(cl,'{"code":"%s","action":"%s","value":"%s"}'%(code,key,value))

def auto_heat(my_agent,turnOn=True):
    key="auto_heat_%s"%("on" if turnOn else "off")
    def h(cl):
        my_agent.set_state_AUTO_HEAT(turnOn)
        if my_agent.get_state_AUTO_HEAT()==turnOn:
            value="true" if turnOn else "false"
            if KV_write("auto_heat",value):
                response_action(cl,key,"OK")
            else:
                response_action(cl,key,"rollback")
                print("[w] rollback")
                my_agent.set_state_AUTO_HEAT(True)
                KV_write("auto_heat","true")
        else:
            response_action(cl,key,"failed")
    return h

def get_state_auto_heat(my_agent):
    def h(cl):
        state=my_agent.get_state_AUTO_HEAT()
        state2=KV_get("auto_heat","")
        # if state2==state:
        response_action(cl,"get_state_auto_heat","OK",'{\\"state\\":{\\"agent\\":\\"%s\\",\\"saved\\":\\"%s\\"}}'%(state,state2))
        # else:
        #     response_action(cl,"get_state_auto_heat","",)
    return h

# def turn_on_auto_heat(cl):
#     auto_heat(cl,True)

# def turn_off_auto_heat(cl):
#     auto_heat(cl,False)

# response_json(cl,'{"code":"OK","action":"reset"}')
def reset_system(cl):
    response_action(cl,"reset","OK")
    cl.close()
    time.sleep(2)
    machine.reset()


def ping(cl):
    response_action(cl,"ping","OK")

def start(my_agent):
    value=KV_get("auto_heat","true")
    state=True
    if value is None:
        pass
    else:
        if value=="true":
            state=True
        elif value=="false":
            state=False
    my_agent.set_state_AUTO_HEAT(state)
    read_mainpage()
    HANDLERS["*"]=mk_404_handler(my_agent)
    HANDLERS["/"]=mk_default_handler(my_agent)
    HANDLERS["/ping"]=ping
    HANDLERS[urls_history]=mk_handler_json(my_agent.get_history)

    HANDLERS["/turn_on_auto_heat"]=auto_heat(my_agent,True)
    HANDLERS["/turn_off_auto_heat"]=auto_heat(my_agent,False)
    HANDLERS["/get_state_auto_heat"]=get_state_auto_heat(my_agent)

    HANDLERS["/reset_system"]=reset_system
    
    mk_handler(my_agent,urls_info,False)
    mk_handler(my_agent,urls_feed,True, lambda : my_agent.setTargetH(2,1))
    mk_handler(my_agent,urls_full_feed,True, lambda : my_agent.setTargetH(4))
    mk_handler(my_agent,urls_cancelfeed,True, lambda : my_agent.setTargetH(-1))
    mk_handler(my_agent,urls_heat,True, lambda : my_agent.setTargetT(64,timestamp=True))
    mk_handler(my_agent,urls_heat_75,True, lambda : my_agent.setTargetT(75,timestamp=True))
    mk_handler(my_agent,urls_cancelheat,True, lambda : my_agent.setTargetT(-1))

    while True:
        #_web_server()
        try:
            addr = socket.getaddrinfo('0.0.0.0', 80)[0][-1]
            s = socket.socket()
            s.bind(addr)
            s.listen(5)

            print('listening on', addr)
            _web_server(s)
        except Exception as e:
            print("[E] web server err",e)
        time.sleep(10)
        try:
            s.close()
        except Exception as e:
            print("[E] web server close err",e)
        time.sleep(5)


